//
// Created by AnKun on 2019/12/3.
//

#include "HashMap.h"

static Entry *newEntry(void);
static HashTable *newHashTable(uint32_t size);
static HashMap *newHashMap(uint32_t size);
static void deleteEntry(Entry *entry);
static void deleteHashTable(HashTable *hashTable);
static void deleteHashMap(HashMap *hashMap);
static uint32_t HashMap_HashCode(const HashMap *const hashMap, const void *key);
static bool HashMap_Equal(const void *key1, const void *key2);

Entry *newEntry(void)
{
    Entry *entry = NULL;
    if ((entry = hmalloc(sizeof(Entry))) != NULL)
    {
        hmemset(entry->key, 0, sizeof(KEY_MAX_LEN));
        hmemset(entry->value, 0, sizeof(VAL_MAX_LEN));
        entry->node.next = NULL;
        entry->node.prev = NULL;
    }
    return entry;
}

void deleteEntry(Entry *entry)
{
    hfree(entry);
    entry = NULL;
}

HashTable *newHashTable(uint32_t size)
{
    uint32_t i = 0;
    HashTable *hashTable = NULL;
    if ((hashTable = hmalloc(sizeof(HashTable) * size)) != NULL)
    {
        for (i = 0; i != size; i++)
        {
            INIT_LIST_HEAD(&hashTable[i]);     // 初始化链表
        }
    }
    return hashTable;
}

void deleteHashTable(HashTable *hashTable)
{
    hfree(hashTable);
    hashTable = NULL;
}

HashMap *newHashMap(uint32_t size)
{
    HashMap *hashMap = NULL;
    if ((hashMap = hmalloc(sizeof(HashMap))) != NULL)
    {
        hashMap->size = size;
        hashMap->used = 0;
        hashMap->items = newHashTable(hashMap->size);
        if (!hashMap->items)
        {
            hfree(hashMap);
            hashMap = NULL;
        }
    }
    return hashMap;
}

void deleteHashMap(HashMap *hashMap)
{
    HashMap_Clear(hashMap);             // 清空列表项
    deleteHashTable(hashMap->items);    // 释放哈希列表空间
    hfree(hashMap);                     // 释放哈希表
    hashMap = NULL;
}

uint32_t HashMap_HashCode(const HashMap *const hashMap, const void *key)
{
    uint8_t *k = (uint8_t *) key;
    unsigned long h = 0;
    while (*k)
    {
        h = (h << 4) + *k++;
        unsigned long g = h & 0xF0000000UL;
        if (g)
        {
            h ^= g >> 24;
        }
        h &= ~g;
    }
    return h % hashMap->size;
}

bool HashMap_Put(HashMap *hashMap, const void *key, const void *value)
{
    uint32_t index = 0;
    Entry *entry = NULL;
    Entry *iterator = NULL;
    HashTable *hashTable = NULL;

    // 空间不足
    if (hashMap->used >= hashMap->size)
        return false;

    // key超长
    if (strlen((const char *) key) > KEY_MAX_LEN)
        return false;

    // value超长
    if (strlen((const char *) value) > VAL_MAX_LEN)
        return false;

    // 申请内存失败直接返回
    if ((entry = newEntry()) == NULL)
        return false;

    // 拷贝到entry中
    strcpy((char *) entry->key, key);
    strcpy((char *) entry->value, value);

    // 计算哈希值获得索引
    index = HashMap_HashCode(hashMap, entry->key);

    // 通过索引定位哈希列表
    hashTable = &hashMap->items[index];

    if (!list_empty(hashTable))
    {
        list_for_each_entry(iterator, hashTable, node)
        {
            bool isKeyExists = HashMap_Equal(iterator->key, entry->key);
            bool isValueExists = HashMap_Equal(iterator->value, entry->value);

            if (!isKeyExists)                                                    // 键不同
            {
                list_add_tail(&entry->node, hashTable);
                return true;
            }
            else if (isKeyExists && !isValueExists)                                     // 键相同，值不同，直接覆盖
            {
                hmemcpy(iterator->value, entry->value, VAL_MAX_LEN);
                deleteEntry(entry);
                return true;
            }
            else if (isKeyExists && isValueExists)                                      // 键与值完全一致
            {
                deleteEntry(entry);
                return true;
            }
        }
    }

    list_add_tail(&entry->node, hashTable);
    hashMap->used++;
    return true;
}

void *HashMap_Get(const HashMap *const hashMap, const void *key)
{
    uint32_t index = 0xFFFFFFFF;
    Entry *iterator = NULL;
    HashTable *hashTable = NULL;

    // 哈希函数计算索引
    index = HashMap_HashCode(hashMap, key);

    // 根据索引拿到哈希链表
    hashTable = &hashMap->items[index];

    // 检测并遍历取对应键的值
    if (!list_empty(hashTable))
    {
        list_for_each_entry(iterator, hashTable, node)
        {
            if (HashMap_Equal(iterator->key, key))
            {
                return (void *) iterator->value;
            }
        }
    }
    return NULL;
}

bool HashMap_Equal(const void *key1, const void *key2)
{
    return strcmp((const char *) key1, (const char *) key2) == 0 ? true : false;
}

void HashMap_Clear(HashMap *hashMap)
{
    uint32_t i = 0;
    Entry *entry = NULL;

    if (hashMap->used != 0)
    {
        for (i = 0; i != hashMap->size; i++)
        {
            while (!list_empty(&hashMap->items[i]))
            {
                entry = list_first_entry(&hashMap->items[i], Entry, node);
                list_del(&entry->node);
                deleteEntry(entry);
            }
        }
        hashMap->used = 0;
    }
}

bool HashMap_Exists(const HashMap *const hashMap, const void *key)
{
    uint32_t index = 0xFFFFFFFF;
    Entry *iterator = NULL;
    HashTable *hashTable = NULL;

    // 计算哈希值得到索引
    index = HashMap_HashCode(hashMap, key);

    // 根据索引定位哈希列表
    hashTable = &hashMap->items[index];

    // 遍历查询
    list_for_each_entry(iterator, hashTable, node)
    {
        if (HashMap_Equal(iterator->key, key))
            return true;
    }

    return false;
}

void HashMap_Remove(HashMap *hashMap, const void *key)
{
    uint32_t index = 0xFFFFFFFF;
    HashTable *hashTable = NULL;
    Entry *entry = NULL;

    // 计算哈希值获得索引
    index = HashMap_HashCode(hashMap, key);

    // 根据索引得到哈希列表
    hashTable = &hashMap->items[index];

    // 遍历查找并删除
    while (!list_empty(hashTable))
    {
        entry = list_first_entry(hashTable, Entry, node);
        if (HashMap_Equal(entry->key, key))
        {
            list_del(&entry->node);
            deleteEntry(entry);
            hashMap->used--;
            break;
        }
    }
}

HashMap *HashMap_Create(uint32_t size)
{
    return newHashMap(size);
}

void HashMap_Destroy(HashMap *hashMap)
{
    deleteHashMap(hashMap);
}

void HashMap_GetKeys(const HashMap *const hashMap, uint8_t **keys, uint32_t *count)
{
    uint32_t i = 0;
    HashTable *hashTable = NULL;

    *count = 0;

    for (i = 0; i != hashMap->size; i++)
    {
        hashTable = &hashMap->items[i];
        if (!list_empty(hashTable))
        {
            Entry *iterator = NULL;
            list_for_each_entry(iterator, hashTable, node)
            {
                keys[(*count)++] = iterator->key;
            }
        }
    }
}
